---
sidebar_position: 2
---


# Приложение 1. О шаблонных классах

## Объявления классов `ListNode` и `MyStack`

```cpp
// Файл MyStack.h
// Шаблонный класс MyStack на основе односвязного списка.
#ifndef MyStack_h           // защита от повторной компиляции
#define MyStack_h           // модуль подключен

// Шаблонный класс ListNode (узел односвязного списка)
template<class INF, class FRIEND>
class ListNode              // узел списка
{
private:
    INF d;                  // информационная часть узла
    ListNode *next;         // указатель на следующий узел списка
    ListNode(void) { next = nullptr; } //конструктор
    friend FRIEND;
};

// Шаблонный класс MyStack на основе односвязного списка.
template<class INF>
class MyStack {
    typedef class ListNode<INF, MyStack<INF>> Node;
    Node *top;
public:
    MyStack(void);           // конструктор
    ~MyStack(void);          // освободить динамическую память
    bool empty(void);        // стек пустой?
    bool push(INF n);        // добавить узел в вершину стека
    bool pop(void);          // удалить узел из вершины стека
    INF top_inf(void);      // считать информацию из вершины стека
};

#endif
```

## Почему мы не можем отделить определение шаблонного класса от его реализации?

:::info
Основано на ответе на вопрос "Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?" из статьи ["Templates | isocpp.org"](https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl)
:::

Чтобы понять, почему все так, как есть, сначала примите эти факты:

- **Шаблон** - это не класс или функция. **Шаблон** - это "образец", который компилятор использует для создания семейства классов или функций.
- Для того, чтобы компилятор сгенерировал код, он должен видеть как реализацию шаблона (а не только определение), так и конкретные типы/все, что используется для "заполнения" шаблона. Например, если вы пытаетесь использовать a `Foo<int>`, компилятор должен видеть как шаблон `Foo`, так и тот факт, что вы пытаетесь создать конкретный `Foo<int>`.
- Ваш компилятор, вероятно, не запоминает детали одного `.cpp` файла во время компиляции другого `.cpp` файла. В то же время, некоторые компиляторы умеют смотреть на весь код программы целиком с целью его оптимизировать. Например, так умеет делать [компилятор LCC](http://www.mcst.ru/lcc) для программирования под процессоры Эльбрус. Смотрите о том, как работает компилятор в [лекции](../../../lectures/cmake/intro.md).

Распространенным решением разделения определения и реализации является запись объявления шаблона в заголовочный файл, затем реализация класса в файле реализации (например `.tpp`) и включение этого файла реализации в конец заголовка. Пример этого содержится в этом [ответе](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file).

Однако авторами методического пособия рекомендуется придерживаться следующей стратегии:

1. Определение и реализацию объявить в заголовочном файле (`MyStack.h`).
2. Создать файл реализации `MyStack.cpp` с импортом заголовочного файла `MyStack.h` (`#include "MyStack.h"`). Это делается для того, чтобы не отклоняться от общего паттерна описания сборки в файле `CMakeLists.txt`.
3. В файле `CMakeLists.txt` при вызове команды `add_library` не забыть передать `MyStack.h` и `MyStack.cpp`.

:::info
См. также дополнительные сведения об использовании шаблонов в [следующей лабораторной работе](../../lab5/Instructions/SupplementMaterial2.md).
:::
